﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;

namespace JinianNet.Net
{
    public class HttpUtils
    {
        private int _timeout;
        private string _method;
        private string _url;
        private string _accept;
        private bool _allowautoredirect;
        private string _errormessage;
        private string _connection;
        private DecompressionMethods _automaticdecompression;
        private string _contenttype;
        private CookieContainer _cookiecontainer;
        private ICredentials _credentials;
        private string _useragent;
        private string _referer;
        private string _ifmodifiedsince;
        private WebHeaderCollection _headers;
        private Encoding _charset;

        public Encoding Charset
        {
            get { return _charset; }
            set { _charset = value; }
        }

        /// <summary>
        /// 超时
        /// </summary>
        public int TimeOut
        {
            get { return _timeout; }
            set { _timeout = value; }
        }
        /// <summary>
        /// Get/Post
        /// </summary>
        public string Method
        {
            get { return _method; }
            set { _method = value; }
        }
        /// <summary>
        /// URL
        /// </summary>
        public string Url
        {
            get { return _url; }
            set { _url = value; }
        }
        /// <summary>
        /// Accept HTTP标头值
        /// </summary>
        public string Accept
        {
            get { return _accept; }
            set { _accept = value; }
        }
        /// <summary>
        /// 是否接受跳转
        /// </summary>
        public bool AllowAutoRedirect
        {
            get { return _allowautoredirect; }
            set { _allowautoredirect = value; }
        }
        /// <summary>
        /// 错误信息
        /// </summary>
        public string ErrorMessage
        {
            get { return _errormessage; }
            private set { _errormessage = value; }
        }
        public string Connection
        {
            get { return _connection; }
            set { _connection = value; }
        }
        public DecompressionMethods AutomaticDecompression
        {
            get { return _automaticdecompression; }
            set { _automaticdecompression = value; }
        }
        public string ContentType
        {
            get { return _contenttype; }
            set { _contenttype = value; }
        }
        public CookieContainer CookieContainer
        {
            get { return _cookiecontainer; }
            set { _cookiecontainer = value; }
        }
        public ICredentials Credentials
        {
            get { return _credentials; }
            set { _credentials = value; }
        }
        public string UserAgent
        {
            get { return _useragent; }
            set { _useragent = value; }
        }
        public string Referer
        {
            get { return _referer; }
            set { _referer = value; }
        }
        public string IfModifiedSince
        {
            get { return _ifmodifiedsince; }
            set { _ifmodifiedsince = value; }
        }
        public WebHeaderCollection Headers
        {
            get { return _headers; }
            set { _headers = value; }
        }

        private Dictionary<string, SendObject> sendDirectory;

        public event EventHandler<SendCompletedEventArgs> SendCompleted;

        //public delegate void SendCompletedEventHandler(object sender, AsyncCompletedEventArgs e);

        protected void OnSendCompleted(SendCompletedEventArgs e)
        {
            if (SendCompleted != null)
            {
                SendCompleted(this, e);
            }
        }

        public HttpUtils()
        {
            TimeOut = 60000;
            Method = "POST";
            AllowAutoRedirect = false;
            AutomaticDecompression = DecompressionMethods.None;
            CookieContainer = new CookieContainer();
            Credentials = null;
            Headers = new WebHeaderCollection();
            sendDirectory = new Dictionary<string, SendObject>();
            this.Charset = Encoding.Default;
        }

        private HttpWebResponse Send(byte[] data, string url)
        {
            if (string.IsNullOrEmpty(url))
                throw new ArgumentException("Url");
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            #region
            request.Timeout = this.TimeOut;
            request.Method = this.Method;
            request.AllowAutoRedirect = this.AllowAutoRedirect;
            request.AutomaticDecompression = this.AutomaticDecompression;
            if (!string.IsNullOrEmpty(this.Accept))
            {
                request.Accept = this.Accept;
            }
            if (!string.IsNullOrEmpty(this.Connection))
            {
                request.Connection = this.Connection;
            }
            if (!string.IsNullOrEmpty(this.ContentType))
            {
                request.ContentType = this.ContentType;
            }
            if (this.CookieContainer.Count > 0)
            {
                request.CookieContainer = this.CookieContainer;
            }
            if (this.Credentials != null)
            {
                request.Credentials = this.Credentials;
            }
            if (!string.IsNullOrEmpty(this.UserAgent))
            {
                request.UserAgent = this.UserAgent;
            }
            if (!string.IsNullOrEmpty(this.Referer))
            {
                request.Referer = this.Referer;
            }
            if (!string.IsNullOrEmpty(this.IfModifiedSince))
            {
                request.IfModifiedSince = DateTime.Parse(this.IfModifiedSince);
            }
            if (this.Headers.Count > 0)
            {
                request.Headers = this.Headers;
            }
            #endregion
            request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
            request.Proxy = null;
            if (data != null && data.Length > 0)
            {
                request.ContentLength = (long)data.Length;
                using (Stream requestStream = request.GetRequestStream())
                {
                    requestStream.Write(data, 0, data.Length);
                    // requestStream.Dispose();
                }
            }

            return (HttpWebResponse)request.GetResponse();
        }

        public void SendAsync()
        {
            SendAsync(null, null);
        }

        public void SendAsync(byte[] data)
        {
            SendAsync(data, null);
        }

        public void SendAsync(byte[] data, object userToken)
        {
            string key = Guid.NewGuid().ToString();

            Thread thread = new Thread(new ParameterizedThreadStart(BeginSendAsync));
            thread.IsBackground = true;

            sendDirectory[key] = new SendObject();
            sendDirectory[key].Data = data;
            sendDirectory[key].Thread = thread;
            sendDirectory[key].UserToken = userToken;

            thread.Start(key);
        }

        private void BeginSendAsync(object value)
        {
            string key = value.ToString();
            SendObject data;
            if (sendDirectory.TryGetValue(key, out data))
            {

                HttpWebResponse response = null;
                SendCompletedEventArgs ev = new SendCompletedEventArgs();
                try
                {
                    response = Send(data.Data, Url);
                }
                catch (WebException e)
                {
                    response = (HttpWebResponse)e.Response;
                    ev.Error = e;
                }
                ev.Response = response;
                ev.UserToken = data.UserToken;
                OnSendCompleted(ev);
                if (response != null)
                {
                    response.Close();
                }
                data.Thread.Abort();
                sendDirectory.Remove(key);
            }
        }

        public string Send()
        {
            return Send(null);
        }


        public string Send(byte[] data)
        {

            string result = null;
            HttpWebResponse response;
            try
            {
                response = Send(data, Url);
            }
            catch (WebException e)
            {
                response = (HttpWebResponse)e.Response;
                ErrorMessage = e.Message;
            }

            if (response.StatusCode == HttpStatusCode.OK)
            {
                using (Stream responseStream = response.GetResponseStream())
                {
                    using (StreamReader reader = new StreamReader(responseStream, Charset))
                    {
                        result = reader.ReadToEnd();
                    }
                }
            }

            if (response != null)
            {
                response.Close();
            }

            return result;
        }

        public class SendCompletedEventArgs : EventArgs
        {
            //public bool Cancelled
            //{
            //    get { return _password; }
            //    set { _password = value; }
            //}
            private Exception _error;
            public Exception Error
            {
                get { return _error; }
                set { _error = value; }
            }
            private HttpWebResponse _response;
            public HttpWebResponse Response
            {
                get { return _response; }
                set { _response = value; }
            }

            private object _userToken;
            public object UserToken
            {
                get { return _userToken; }
                set { _userToken = value; }
            }
        }

        private class SendObject
        {
            private Thread _thread;
            public Thread Thread
            {
                get { return _thread; }
                set { _thread = value; }
            }

            private byte[] _data;
            public byte[] Data
            {
                get { return _data; }
                set { _data = value; }
            }

            private object _userToken;
            public object UserToken
            {
                get { return _userToken; }
                set { _userToken = value; }
            }

        }
    }
}
